package manager

import (
	"context"
	"fmt"

	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/client-go/kubernetes"
)

type Config struct {
	PreviewerPoolNamespace string
	PreviewerPoolSize      int
	PreviewerImageName     string
	SidecarImageName       string
	ImagePullSecretName    string
	SidecarPort int
	PreviewerPort int

	Addr string
	Port int
}

func (pm *PreviewerManager) Init() error {
	pm.Lock()
	defer pm.Unlock()
	cfg := pm.cfg
	kubecli, _, err := GetKubeClientInCluster()
	if err != nil {
		return fmt.Errorf("get kube client err, %v", err)
	}

	// ensure namespace
	if err = EnsurePoolNamespace(kubecli, cfg.PreviewerPoolNamespace); err != nil {
		return err
	}

	var previewerEnv, sidecarEnv map[string]string
	sidecarEnv = map[string]string{
		"Sidecar_Addr":"0.0.0.0",
		"Sidecar_Port": fmt.Sprintf("%v", cfg.SidecarPort),
		"Sidecar_PreviewerPort": fmt.Sprintf("%v", cfg.PreviewerPort),
	}
	// TODO: collect previewer config
	// TODO: collect sidecar config

	var previewerPorts, sidecarPorts map[string]int32
	previewerPorts = map[string]int32{
		"previewer-port": int32(cfg.PreviewerPort),
	}
	sidecarPorts = map[string]int32{
		"sidecar-port": int32(cfg.SidecarPort),
	}

	var servicePorts map[int32]int32
	servicePorts = map[int32]int32{
		int32(cfg.PreviewerPort): int32(cfg.PreviewerPort),
		int32(cfg.SidecarPort): int32(cfg.SidecarPort),
	}

	// build cache
	pm.idle = NewQueue(cfg.PreviewerPoolSize + 1)

	// build previewer pool
	for i := 0; i < cfg.PreviewerPoolSize; i++ {
		previewerId := GenPreviewerId(i)
		fmt.Printf("create previewer: %v\n", previewerId)

		// check exists and delete old
		if _, err = GetDeployment(kubecli, context.Background(), previewerId, cfg.PreviewerPoolNamespace); err != nil {
			if !errors.IsNotFound(err) {
				// get deployment err
				return fmt.Errorf("get deployment [%v.%v] err, %v", previewerId, cfg.PreviewerPoolNamespace, err)
			}
		} else { // delete exists deployment
			err = DeleteDeployment(kubecli, context.Background(), previewerId, cfg.PreviewerPoolNamespace)
			if err != nil {
				return fmt.Errorf("delete exists deployment [%v.%v] err, %v", previewerId, cfg.PreviewerPoolNamespace, err)
			}
		}

		err = CreateDeployment(
			kubecli,
			context.Background(),
			previewerId,
			cfg.PreviewerPoolNamespace,
			cfg.PreviewerImageName,
			cfg.SidecarImageName,
			cfg.ImagePullSecretName,
			previewerEnv,
			sidecarEnv,
			previewerPorts,
			sidecarPorts,
		)
		if err != nil {
			return fmt.Errorf("create previewer deployment of [%v] err, %v", previewerId, err)
		}

		if len(servicePorts) == 0 {
			continue
		}

		// check exists and delete old
		if _, err = GetService(kubecli, context.Background(), previewerId, cfg.PreviewerPoolNamespace); err != nil {
			if !errors.IsNotFound(err) {
				// get service err
				return fmt.Errorf("get service [%v.%v] err, %v", previewerId, cfg.PreviewerPoolNamespace, err)
			}
		} else { // delete exists service
			err = DeleteService(kubecli, context.Background(), previewerId, cfg.PreviewerPoolNamespace)
			if err != nil {
				return fmt.Errorf("delete exists service [%v.%v] err, %v", previewerId, cfg.PreviewerPoolNamespace, err)
			}
		}

		err = CreateService(
			kubecli,
			context.Background(),
			previewerId,
			cfg.PreviewerPoolNamespace,
			servicePorts,
		)
		if err != nil {
			return fmt.Errorf("create previewer service of [%v] err, %v", previewerId, err)
		}
		if err = pm.idle.Push(previewerId); err != nil {
			return err
		}
	}

	return nil
}

func EnsurePoolNamespace(kubecli *kubernetes.Clientset, name string) error {
	_, err := GetNamespace(kubecli, context.Background(), name)
	if err != nil {
		if errors.IsNotFound(err) {
			// create namespace
			return CreateNamespace(kubecli, context.Background(), name)
		}
		return err
	}
	return nil
}
